/*
                                  Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control inject that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      inject the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, inject or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along inject the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies inject
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      inject Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated inject Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent inject this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, inject the fields enclosed by brackets "{}"
      replaced inject your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {razerdp}

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance inject the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */
package com.harvest.scientific.ladder.pop.basepopup;

import static com.harvest.scientific.ladder.pop.basepopup.BasePopupFlag.AUTO_INPUT_METHOD;
import static com.harvest.scientific.ladder.pop.basepopup.BasePopupFlag.AUTO_MIRROR;
import static com.harvest.scientific.ladder.pop.basepopup.BasePopupFlag.BACKPRESS_ENABLE;
import static com.harvest.scientific.ladder.pop.basepopup.BasePopupFlag.CLIP_CHILDREN;
import static com.harvest.scientific.ladder.pop.basepopup.BasePopupFlag.FADE_ENABLE;
import static com.harvest.scientific.ladder.pop.basepopup.BasePopupFlag.FITSIZE;
import static com.harvest.scientific.ladder.pop.basepopup.BasePopupFlag.MODE_ADD;
import static com.harvest.scientific.ladder.pop.basepopup.BasePopupFlag.MODE_REMOVE;
import static com.harvest.scientific.ladder.pop.basepopup.BasePopupFlag.OUT_SIDE_DISMISS;
import static com.harvest.scientific.ladder.pop.basepopup.BasePopupFlag.OUT_SIDE_TOUCHABLE;

import android.animation.Animator;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.Message;
import android.util.AndroidRuntimeException;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.widget.EditText;
import android.widget.PopupWindow;

import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer;
import androidx.lifecycle.OnLifecycleEvent;

import com.harvest.scientific.ladder.R;
import com.harvest.scientific.ladder.pop.blur.PopupBlurOption;
import com.harvest.scientific.ladder.pop.util.KeyboardUtils;
import com.harvest.scientific.ladder.pop.util.PopupUiUtils;
import com.harvest.scientific.ladder.pop.util.PopupUtils;

public abstract class BasePopupWindow implements PopupWindow.OnDismissListener, LifecycleObserver {
    static final String TAG = "BasePopupWindow";
    public static int DEFAULT_BACKGROUND_COLOR = Color.parseColor("#8f000000");

    public static final int FLAG_KEYBOARD_ALIGN_TO_VIEW = BasePopupFlag.KEYBOARD_ALIGN_TO_VIEW;
    public static final int FLAG_KEYBOARD_ALIGN_TO_ROOT = BasePopupFlag.KEYBOARD_ALIGN_TO_ROOT;
    public static final int FLAG_KEYBOARD_IGNORE_OVER = BasePopupFlag.KEYBOARD_IGNORE_OVER_KEYBOARD;
    public static final int FLAG_KEYBOARD_ANIMATE_ALIGN = BasePopupFlag.KEYBOARD_ANIMATE_ALIGN;
    public static final int FLAG_KEYBOARD_FORCE_ADJUST = BasePopupFlag.KEYBOARD_FORCE_ADJUST;

    public enum GravityMode {
        RELATIVE_TO_ANCHOR,
        ALIGN_TO_ANCHOR_SIDE
    }

    public enum Priority {
        NORMAL(2),
        HIGH(5),
        LOW(0);

        int type;

        Priority(int windowType) {
            this.type = windowType;
        }
    }

    private static final int MAX_RETRY_SHOW_TIME = 3;
    public static final int MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT;
    public static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;

    private View mAnchorDecorView;
    private boolean isDestroyed;

    BasePopupHelper mHelper;
    Activity mContext;

    Object ownerAnchorParent;
    boolean pendingPopupWindow;

    PopupWindowProxy mPopupWindowProxy;
    View mContentView;
    View mDisplayAnimateView;
    int initWidth;
    int initHeight;

    Runnable initRunnable;

    private volatile boolean isExitAnimatePlaying = false;
    public BasePopupWindow(Context context) {
        this(context, 0, 0);
    }

    public BasePopupWindow(Context context, int width, int height) {
        this(context, width, height, 0);
    }

    public BasePopupWindow(Fragment fragment) {
        this(fragment, 0, 0);
    }

    public BasePopupWindow(Fragment fragment, int width, int height) {
        this(fragment, width, height, 0);
    }

    public BasePopupWindow(Dialog dialog) {
        this(dialog, 0, 0);
    }

    public BasePopupWindow(Dialog dialog, int width, int height) {
        this(dialog, width, height, 0);
    }

    BasePopupWindow(final Object ownerAnchorParent, final int width, final int height, int flag) {
        this.ownerAnchorParent = ownerAnchorParent;
        checkActivity();
        mHelper = new BasePopupHelper(this);
        setPriority(Priority.NORMAL);
        initWidth = width;
        initHeight = height;
    }

    private void checkActivity() {
        if (mContext != null) {
            return;
        }
        Activity act = BasePopupHelper.findActivity(ownerAnchorParent);
        if (act == null) return;
        if (ownerAnchorParent instanceof LifecycleOwner) {
            bindLifecycleOwner((LifecycleOwner) ownerAnchorParent);
        } else if (act instanceof LifecycleOwner) {
            bindLifecycleOwner((LifecycleOwner) act);
        } else {
            listenForLifeCycle(act);
        }
        mContext = act;
        if (initRunnable != null) {
            initRunnable.run();
        }
    }

    public BasePopupWindow bindLifecycleOwner(LifecycleOwner lifecycleOwner) {
        if (getContext() instanceof LifecycleOwner) {
            ((LifecycleOwner) getContext()).getLifecycle().removeObserver(this);
        }
        lifecycleOwner.getLifecycle().addObserver(this);
        return this;
    }

    private void listenForLifeCycle(Activity act) {
        act.getWindow()
                .getDecorView()
                .addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
                    @Override
                    public void onViewAttachedToWindow(View v) {

                    }

                    @Override
                    public void onViewDetachedFromWindow(View v) {
                        v.removeOnAttachStateChangeListener(this);
                        onDestroy();
                    }
                });
    }

    void initView(View contentView) {
        mContentView = contentView;
        mHelper.setContentRootId(mContentView);
        mDisplayAnimateView = onCreateAnimateView();
        if (mDisplayAnimateView == null) {
            mDisplayAnimateView = mContentView;
        }

        setWidth(initWidth);
        setHeight(initHeight);

        if (mPopupWindowProxy == null) {
            mPopupWindowProxy = new PopupWindowProxy(new PopupWindowProxy.BasePopupContextWrapper(
                    getContext(),
                    mHelper));
        }
        mPopupWindowProxy.setContentView(mContentView);
        mPopupWindowProxy.setOnDismissListener(this);
        setPopupAnimationStyle(0);

        if (mContentView != null) {
            onViewCreated(mContentView);
        }
    }

    public void onViewCreated(@NonNull View contentView) {

    }

    public void setContentView(@LayoutRes int layoutResID) {
        this.setContentView(createPopupById(layoutResID));
    }

    public void setContentView(final View view) {
        initRunnable = new Runnable() {
            @Override
            public void run() {
                initRunnable = null;
                initView(view);
            }
        };
        if (getContext() == null) return;
        initRunnable.run();
    }

    protected Animation onCreateShowAnimation() {
        return null;
    }

    protected Animation onCreateShowAnimation(int width, int height) {
        return onCreateShowAnimation();
    }

    protected Animation onCreateDismissAnimation() {
        return null;
    }

    protected Animation onCreateDismissAnimation(int width, int height) {
        return onCreateDismissAnimation();
    }

    protected Animator onCreateShowAnimator() {
        return null;
    }

    protected Animator onCreateShowAnimator(int width, int height) {
        return onCreateShowAnimator();
    }

    protected View onCreateAnimateView() {
        return null;
    }


    protected Animator onCreateDismissAnimator() {
        return null;
    }

    protected Animator onCreateDismissAnimator(int width, int height) {
        return onCreateDismissAnimator();
    }

    public BasePopupWindow setPopupFadeEnable(boolean isPopupFadeAnimate) {
        mHelper.setFlag(FADE_ENABLE, isPopupFadeAnimate);
        return this;
    }

    public boolean isPopupFadeEnable() {
        return mHelper.isPopupFadeEnable();
    }

    public BasePopupWindow setPopupAnimationStyle(int animationStyleRes) {
        mHelper.animationStyleRes = animationStyleRes;
        return this;
    }


    public void showPopupWindow() {
        if (checkPerformShow(null)) {
            mHelper.withAnchor(false);
            tryToShowPopup(null, false);
        }
    }

    public void showPopupWindow(View anchorView) {
        if (checkPerformShow(anchorView)) {
            mHelper.withAnchor(anchorView != null);
            tryToShowPopup(anchorView, false);
        }
    }

    public void showPopupWindow(int x, int y) {
        if (checkPerformShow(null)) {
            mHelper.setShowLocation(x, y);
            mHelper.withAnchor(true);
            tryToShowPopup(null, true);
        }
    }

    public void update() {
        mHelper.update(null, false);
    }

    public void update(View anchorView) {
        mHelper.update(anchorView, false);
    }

    public void update(int x, int y) {
        if (!isShowing() || getContentView() == null) return;
        mHelper.setShowLocation(x, y);
        mHelper.withAnchor(true);
        mHelper.update(null, true);
    }

    public void update(float width, float height) {
        if (!isShowing() || getContentView() == null) return;
        setWidth((int) width)
                .setHeight((int) height)
                .update();

    }

    public void update(int x, int y, float width, float height) {
        if (!isShowing() || getContentView() == null) return;
        mHelper.setShowLocation(x, y);
        mHelper.withAnchor(true);
        mHelper.setPopupViewWidth((int) width);
        mHelper.setPopupViewHeight((int) height);
        mHelper.update(null, true);
    }

    void tryToShowPopup(View v, boolean positionMode) {
        mHelper.enablePendingDismiss = true;
        checkActivity();
        if (mContext == null) {
            if (BasePopupSDK.getInstance().getTopActivity() == null) {
                waitForFirstActivityOpened(v, positionMode);
                return;
            }
            return;
        }
        if (isShowing() || mContentView == null) return;
        if (isDestroyed) {
            return;
        }
        View decorView = getDecorView();
        if (decorView == null) {
            return;
        }
        if (decorView.getWindowToken() == null) {
            pendingPopupWindow(decorView, v, positionMode);
            return;
        }
        if (!onBeforeShow()) return;
        mHelper.prepare(v, positionMode);
        try {
            if (isShowing()) {
                return;
            }
            mHelper.onShow();
            mPopupWindowProxy.showAtLocation(decorView, Gravity.NO_GRAVITY, 0, 0);
        } catch (Exception e) {
            superDismiss();
        }
    }

    void waitForFirstActivityOpened(View v, boolean positionMode) {
        BasePopupSDK.getInstance().regFirstActivityOpen(new Observer<Boolean>() {
            @Override
            public void onChanged(Boolean aBoolean) {
                tryToShowPopup(v, positionMode);
            }
        });
    }

    final boolean onBeforeDismissInternal(@Nullable OnDismissListener cb) {
        boolean result = onBeforeDismiss();
        if (cb != null) {
            result = result && cb.onBeforeDismiss();
        }
        return result;
    }

    public boolean onBeforeShow() {
        return true;
    }

    public boolean onBeforeDismiss() {
        return true;
    }

    @Nullable
    private View getDecorView() {
        mAnchorDecorView = BasePopupHelper.findDecorView(ownerAnchorParent);
        return mAnchorDecorView;
    }

    void dispatchOutSideEvent(MotionEvent event, boolean touchInBackground, boolean isMaskPressed) {
        boolean consumeEvent = onOutSideTouch(event, touchInBackground, isMaskPressed);
        if (mHelper.isOutSideTouchable()) {
            WindowManagerProxy proxy = mPopupWindowProxy.prevWindow();
            if (proxy == null) {
                if (consumeEvent) {
                    event.setAction(MotionEvent.ACTION_CANCEL);
                }
                if (mAnchorDecorView != null) {
                    mAnchorDecorView.getRootView().dispatchTouchEvent(event);
                } else {
                    mContext.getWindow().getDecorView().getRootView().dispatchTouchEvent(event);
                }
            } else {
                if (!consumeEvent) {
                    proxy.dispatchToDecorProxy(event);
                }
            }
        }
    }


    private void pendingPopupWindow(@NonNull View decorView, @Nullable final View anchorView, final boolean positionMode) {
        if (pendingPopupWindow) return;
        pendingPopupWindow = true;
        decorView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
            @Override
            public void onViewAttachedToWindow(View v) {
                pendingPopupWindow = false;
                v.removeOnAttachStateChangeListener(this);
                v.post(new Runnable() {
                    @Override
                    public void run() {
                        tryToShowPopup(anchorView, positionMode);
                    }
                });

            }

            @Override
            public void onViewDetachedFromWindow(View v) {
                pendingPopupWindow = false;
                v.removeOnAttachStateChangeListener(this);
            }
        });
    }


    /**
     * PopupWindow展示时是否收起键盘，默认收起
     *
     * @param dismiss
     * @return
     */
    public BasePopupWindow hideKeyboardOnShow(boolean dismiss) {
        setKeyboardAdaptive(dismiss);
        return this;
    }

    /**
     * PopupWindow消失时是否收起键盘，默认收起
     *
     * @param dismiss
     * @return
     */
    public BasePopupWindow hideKeyboardOnDismiss(boolean dismiss) {
        mHelper.hideKeyboardOnDismiss = dismiss;
        return this;
    }

    /**
     * <p>
     * PopupWindow是否需要自适应输入法，为输入法弹出让出区域
     * </p>
     *
     * @param adaptive <ul>
     *                 <li>true for "SOFT_INPUT_ADJUST_RESIZE" mode</li>
     *                 <li>false for "SOFT_INPUT_STATE_UNCHANGED" mode</li>
     *                 </ul>
     *                 <br>
     */
    public BasePopupWindow setKeyboardAdaptive(boolean adaptive) {
        mHelper.mSoftInputMode = adaptive ? WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE : WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
        return this;
    }

    public BasePopupWindow setKeyboardAdaptionMode(int mode) {
        return setKeyboardAdaptionMode(0, mode);
    }

    public BasePopupWindow setKeyboardAdaptionMode(int viewId, int mode) {
        mHelper.keybaordAlignViewId = viewId;
        mHelper.setFlag(FLAG_KEYBOARD_ALIGN_TO_ROOT
                | FLAG_KEYBOARD_ALIGN_TO_VIEW
                | FLAG_KEYBOARD_IGNORE_OVER
                | FLAG_KEYBOARD_ANIMATE_ALIGN
                | FLAG_KEYBOARD_FORCE_ADJUST, false);
        mHelper.setFlag(mode, true);
        return this;
    }

    public BasePopupWindow setKeyboardAdaptionMode(View alignTarget, int mode) {
        mHelper.keybaordAlignView = alignTarget;
        mHelper.setFlag(FLAG_KEYBOARD_ALIGN_TO_ROOT
                | FLAG_KEYBOARD_ALIGN_TO_VIEW
                | FLAG_KEYBOARD_IGNORE_OVER
                | FLAG_KEYBOARD_ANIMATE_ALIGN
                | FLAG_KEYBOARD_FORCE_ADJUST, false);
        mHelper.setFlag(mode, true);
        return this;
    }

    public BasePopupWindow setAutoShowKeyboard(boolean autoShow) {
        return setAutoShowKeyboard(null, autoShow);
    }

    public BasePopupWindow setAutoShowKeyboard(EditText editText, boolean autoShow) {
        mHelper.mAutoShowInputEdittext = editText;
        mHelper.setFlag(AUTO_INPUT_METHOD, autoShow);
        return this;
    }

    public BasePopupWindow setShowKeyboardDelay(long delay) {
        mHelper.showKeybaordDelay = Math.max(0, delay);
        return this;
    }

    public BasePopupWindow setKeyboardGravity(int gravity) {
        mHelper.keyboardGravity = gravity;
        return this;
    }

    public BasePopupWindow setKeyboardOffsetX(int offsetX) {
        mHelper.keyboardOffsetX = offsetX;
        return this;
    }

    public BasePopupWindow setKeyboardOffsetY(int offsetY) {
        mHelper.keyboardOffsetY = offsetY;
        return this;
    }


    public BasePopupWindow setBackPressEnable(boolean backPressEnable) {
        mHelper.setFlag(BACKPRESS_ENABLE, backPressEnable);
        return this;
    }

    public View createPopupById(int layoutId) {
        return mHelper.inflate(getContextInner(true), layoutId);
    }

    public <T extends View> T findViewById(int id) {
        if (mContentView != null && id != 0) {
            return (T) mContentView.findViewById(id);
        }
        return null;
    }

    public BasePopupWindow setOverlayStatusbar(boolean overlay) {
        mHelper.overlayStatusbar(overlay);
        return this;
    }

    public BasePopupWindow setOverlayStatusbarMode(int mode) {
        mHelper.setOverlayStatusbarMode(mode);
        return this;
    }

    public BasePopupWindow setOverlayNavigationBar(boolean overlay) {
        mHelper.overlayNavigationBar(overlay);
        return this;
    }

    public BasePopupWindow setOverlayMask(boolean overlay) {
        mHelper.overlayMask = overlay;
        return this;
    }

    public BasePopupWindow setOverlayNavigationBarMode(int mode) {
        mHelper.setOverlayNavigationBarMode(mode);
        return this;
    }

    public BasePopupWindow setBackgroundColor(int color) {
        mHelper.setPopupBackground(new ColorDrawable(color));
        return this;
    }

    public BasePopupWindow setBackground(int drawableIds) {
        if (drawableIds == 0) {
            return setBackground(null);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return setBackground(getContextInner(true).getDrawable(drawableIds));
        } else {
            return setBackground(getContextInner(true).getResources().getDrawable(drawableIds));
        }
    }

    public BasePopupWindow setBackground(Drawable background) {
        mHelper.setPopupBackground(background);
        return this;
    }

    public BasePopupWindow setBackgroundView(View backgroundView) {
        mHelper.setBackgroundView(backgroundView);
        return this;
    }

    public Drawable getPopupBackground() {
        return mHelper.getPopupBackground();
    }

    public BasePopupWindow setBlurBackgroundEnable(boolean blurBackgroundEnable) {
        return setBlurBackgroundEnable(blurBackgroundEnable, null);
    }

    public BasePopupWindow setBlurBackgroundEnable(boolean blurBackgroundEnable, OnBlurOptionInitListener optionInitListener) {
        Activity activity = getContext();
        if (activity == null) {
            return this;
        }
        PopupBlurOption option = null;
        if (blurBackgroundEnable) {
            option = new PopupBlurOption();
            option.setFullScreen(true)
                    .setBlurInDuration(-1)
                    .setBlurOutDuration(-1);
            if (optionInitListener != null) {
                optionInitListener.onCreateBlurOption(option);
            }
            View decorView = getDecorView();
            if (decorView instanceof ViewGroup && decorView.getId() == android.R.id.content) {
                option.setBlurView(((ViewGroup) activity.getWindow().getDecorView()).getChildAt(0));
                option.setFullScreen(true);
            } else {
                option.setBlurView(decorView);
            }
        }

        return setBlurOption(option);
    }

    public BasePopupWindow setBlurOption(PopupBlurOption option) {
        mHelper.setToBlur(option);
        return this;
    }

    protected void setViewClickListener(View.OnClickListener listener, View... views) {
        for (View view : views) {
            if (view != null && listener != null) {
                view.setOnClickListener(listener);
            }
        }
    }

    public boolean isShowing() {
        if (mPopupWindowProxy == null) {
            return false;
        }
        return mPopupWindowProxy.isShowing() || (mHelper.showFlag & BasePopupHelper.STATUS_START_SHOWING) != 0;
    }

    public OnDismissListener getOnDismissListener() {
        return mHelper.mOnDismissListener;
    }

    public BasePopupWindow setOnPopupWindowShowListener(OnPopupWindowShowListener onPopupWindowShowListener) {
        mHelper.mOnPopupWindowShowListener = onPopupWindowShowListener;
        return this;
    }

    public BasePopupWindow setOnDismissListener(OnDismissListener onDismissListener) {
        mHelper.mOnDismissListener = onDismissListener;
        return this;
    }

    public OnBeforeShowCallback getOnBeforeShowCallback() {
        return mHelper.mOnBeforeShowCallback;
    }

    public BasePopupWindow setOnBeforeShowCallback(OnBeforeShowCallback mOnBeforeShowCallback) {
        mHelper.mOnBeforeShowCallback = mOnBeforeShowCallback;
        return this;
    }

    public BasePopupWindow setShowAnimation(Animation showAnimation) {
        mHelper.setShowAnimation(showAnimation);
        return this;
    }

    public Animation getShowAnimation() {
        return mHelper.mShowAnimation;
    }

    public BasePopupWindow setShowAnimator(Animator showAnimator) {
        mHelper.setShowAnimator(showAnimator);
        return this;
    }

    public Animator getShowAnimator() {
        return mHelper.mShowAnimator;
    }

    public BasePopupWindow setDismissAnimation(Animation dismissAnimation) {
        mHelper.setDismissAnimation(dismissAnimation);
        return this;
    }

    public Animation getDismissAnimation() {
        return mHelper.mDismissAnimation;
    }

    public BasePopupWindow setDismissAnimator(Animator dismissAnimator) {
        mHelper.setDismissAnimator(dismissAnimator);
        return this;
    }

    public Animator getDismissAnimator() {
        return mHelper.mDismissAnimator;
    }

    public Activity getContext() {
        return mContext;
    }

    @Nullable
    Context getContextInner(boolean appContextIfNoContext) {
        Context ctx = getContext();
        if (ctx == null && appContextIfNoContext) {
            ctx = BasePopupSDK.getApplication();
        }
        return ctx;
    }

    public View getContentView() {
        return mContentView;
    }

    public View getDisplayAnimateView() {
        return mDisplayAnimateView;
    }

    public PopupWindow getPopupWindow() {
        return mPopupWindowProxy;
    }

    public int getOffsetX() {
        return mHelper.getOffsetX();
    }

    public int getMaskOffsetX() {
        return mHelper.maskOffsetX;
    }

    public BasePopupWindow setOffsetX(int offsetX) {
        mHelper.offsetX = offsetX;
        return this;
    }

    public BasePopupWindow setMaskOffsetX(int offsetX) {
        mHelper.maskOffsetX = offsetX;
        return this;
    }

    public int getOffsetY() {
        return mHelper.getOffsetY();
    }

    public int getMaskOffsetY() {
        return mHelper.maskOffsetY;
    }

    public BasePopupWindow setOffsetY(int offsetY) {
        mHelper.offsetY = offsetY;
        return this;
    }

    public BasePopupWindow setMaskOffsetY(int offsetY) {
        mHelper.maskOffsetY = offsetY;
        return this;
    }

    public int getPopupGravity() {
        return mHelper.getPopupGravity();
    }

    public BasePopupWindow setPopupGravity(int popupGravity) {
        mHelper.popupGravity = popupGravity;
        return this;
    }

    public BasePopupWindow setPopupGravity(GravityMode mode, int popupGravity) {
        mHelper.setPopupGravity(mode, popupGravity);
        return this;
    }

    public BasePopupWindow setPopupGravityMode(GravityMode mode) {
        mHelper.setPopupGravityMode(mode, mode);
        return this;
    }

    public BasePopupWindow setPopupGravityMode(GravityMode horizontalMode, GravityMode verticalMode) {
        mHelper.setPopupGravityMode(horizontalMode, verticalMode);
        return this;
    }

    public boolean isAutoMirror() {
        return mHelper.isAutoMirror();
    }

    public BasePopupWindow setAutoMirrorEnable(boolean enable) {
        mHelper.setFlag(AUTO_MIRROR, enable);
        mHelper.cacheFlag(FITSIZE, true);
        if (enable) {
            setFitSize(false);
        } else {
            setFitSize(mHelper.restoreFlag(FITSIZE, true));
        }
        return this;
    }

    public int getHeight() {
        if (mContentView == null) return 0;
        return mContentView.getHeight();
    }

    public int getWidth() {
        if (mContentView == null) return 0;
        return mContentView.getWidth();
    }

    public void preMeasure(int width, int height) {
        mHelper.preMeasurePopupView(mContentView, width, height);
    }

    public int getPreMeasureWidth() {
        return mHelper.preMeasureWidth;
    }

    public int getPreMeasureHeight() {
        return mHelper.preMeasureHeight;
    }

    public BasePopupWindow setOutSideDismiss(boolean outSideDismiss) {
        mHelper.setFlag(OUT_SIDE_DISMISS, outSideDismiss);
        return this;
    }

    public BasePopupWindow setOutSideTouchable(boolean touchable) {
        mHelper.setFlag(OUT_SIDE_TOUCHABLE, touchable);
        return this;
    }

    public BasePopupWindow setClipChildren(boolean clipChildren) {
        mHelper.setFlag(CLIP_CHILDREN, clipChildren);
        return this;
    }

    public boolean isAllowDismissWhenTouchOutside() {
        return mHelper.isOutSideDismiss();
    }

    public boolean isOutSideTouchable() {
        return mHelper.isOutSideTouchable();
    }

    public BasePopupWindow setAlignBackground(boolean isAlignBackground) {
        mHelper.setAlignBackgound(isAlignBackground);
        return this;
    }

    public BasePopupWindow setAlignBackgroundGravity(int gravity) {
        mHelper.setAlignBackgroundGravity(gravity);
        return this;
    }

    public BasePopupWindow linkTo(View anchorView) {
        mHelper.linkTo(anchorView);
        return this;
    }

    public BasePopupWindow setWidth(int width) {
        mHelper.setPopupViewWidth(width);
        return this;
    }

    public BasePopupWindow setHeight(int height) {
        mHelper.setPopupViewHeight(height);
        return this;
    }

    public BasePopupWindow setMaxWidth(int maxWidth) {
        mHelper.maxWidth = maxWidth;
        return this;
    }

    public BasePopupWindow setMaxHeight(int maxHeight) {
        mHelper.maxHeight = maxHeight;
        return this;
    }

    public BasePopupWindow setMinWidth(int minWidth) {
        mHelper.minWidth = minWidth;
        return this;
    }

    public BasePopupWindow setMinHeight(int minHeight) {
        mHelper.minHeight = minHeight;
        return this;
    }

    public BasePopupWindow setFitSize(boolean canResize) {
        mHelper.setFlag(FITSIZE, canResize);
        return this;
    }

    public BasePopupWindow setMaskViewShowAnimation(Animation animation) {
        mHelper.mMaskViewShowAnimation = animation;
        mHelper.isDefaultMaskViewShowAnimation = false;
        return this;
    }

    public BasePopupWindow setMaskViewDismissAnimation(Animation animation) {
        mHelper.mMaskViewDismissAnimation = animation;
        mHelper.isDefaultMaskViewDismissAnimation = false;
        return this;
    }

    public BasePopupWindow syncMaskAnimationDuration(boolean sync) {
        mHelper.setFlag(BasePopupFlag.SYNC_MASK_ANIMATION_DURATION, sync);
        return this;
    }

    public BasePopupWindow setOnKeyboardChangeListener(KeyboardUtils.OnKeyboardChangeListener listener) {
        mHelper.mUserKeyboardStateChangeListener = listener;
        return this;
    }

    public BasePopupWindow setKeyEventListener(KeyEventListener keyEventListener) {
        mHelper.mKeyEventListener = keyEventListener;
        return this;
    }

    public BasePopupWindow setWidthAsAnchorView(boolean widthAsAnchor) {
        mHelper.setFlag(BasePopupFlag.AS_WIDTH_AS_ANCHOR, widthAsAnchor);
        return this;
    }

    public BasePopupWindow setHeightAsAnchorView(boolean heightAsAnchor) {
        mHelper.setFlag(BasePopupFlag.AS_HEIGHT_AS_ANCHOR, heightAsAnchor);
        return this;
    }

    public BasePopupWindow setLayoutDirection(int layoutDirection) {
        mHelper.layoutDirection = layoutDirection;
        return this;
    }

    public BasePopupWindow setTouchable(boolean touchable) {
        mHelper.setFlag(BasePopupFlag.TOUCHABLE, touchable);
        if (isShowing()) {
            ((PopupWindowProxy) getPopupWindow()).updateFlag(touchable ? MODE_REMOVE : MODE_ADD,
                    true,
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
        }
        return this;
    }

    public boolean isTouchable() {
        return (mHelper.flag & BasePopupFlag.TOUCHABLE) != 0;
    }

    public BasePopupWindow updateKeyboardAlign() {
        Message msg = Message.obtain();
        msg.what = BasePopupEvent.EVENT_ALIGN_KEYBOARD;
        mHelper.sendEvent(msg);
        return this;
    }

    public BasePopupWindow setPriority(Priority priority) {
        mHelper.priority = priority == null ? Priority.NORMAL : priority;
        return this;
    }

    void superDismiss() {
        try {
            mPopupWindowProxy.superDismiss();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mHelper.onDismiss();
        }
    }

    public void dismiss() {
        dismiss(true);
    }

    public void dismiss(boolean animateDismiss) {
        if (mContentView == null) return;
        if (!isShowing()) {
            mHelper.pendingDismiss(animateDismiss);
        } else {
            mHelper.dismiss(animateDismiss);
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
        isDestroyed = true;
        mHelper.forceDismiss();
        if (mPopupWindowProxy != null) {
            mPopupWindowProxy.clear(true);
        }
        if (mHelper != null) {
            mHelper.clear(true);
        }
        initRunnable = null;
        ownerAnchorParent = null;
        mAnchorDecorView = null;
        mPopupWindowProxy = null;
        mDisplayAnimateView = null;
        mContentView = null;
        mContext = null;
    }

    private boolean checkPerformShow(View v) {
        boolean result = true;
        if (mHelper.mOnBeforeShowCallback != null) {
            result = mHelper.mOnBeforeShowCallback.onBeforeShow(mContentView, v,
                    mHelper.mShowAnimation != null || mHelper.mShowAnimator != null);
        }
        return result;
    }

    public boolean onDispatchKeyEvent(KeyEvent event) {
        return false;
    }

    public boolean onInterceptTouchEvent(MotionEvent event) {
        return false;
    }

    public boolean onTouchEvent(MotionEvent event) {
        return false;
    }

    public boolean onBackPressed() {
        if (mHelper.isBackPressEnable()) {
            dismiss();
            return true;
        }
        return false;
    }

    public boolean onGenericMotionEvent(MotionEvent event) {
        return false;
    }

    public boolean onOutSideTouch(MotionEvent event, boolean touchInBackground, boolean isPressed) {
        if (mHelper.isOutSideDismiss() && event.getAction() == MotionEvent.ACTION_UP && isPressed) {
            dismiss();
            return true;
        }
        return false;
    }

    public void onWindowFocusChanged(View popupDecorViewProxy, boolean hasWindowFocus) {

    }

    public void onAnchorTop() {

    }

    public void onAnchorBottom() {

    }

    @Override
    public void onDismiss() {
        if (mHelper.mOnDismissListener != null) {
            mHelper.mOnDismissListener.onDismiss();
        }
        isExitAnimatePlaying = false;
    }

    public void onShowing() {

    }

    public void onPopupLayout(@NonNull Rect popupRect, @NonNull Rect anchorRect) {

    }

    public int computeGravity(@NonNull Rect popupRect, @NonNull Rect anchorRect) {
        return PopupUiUtils.computeGravity(popupRect, anchorRect);
    }

    public void onSizeChange(int oldW, int oldH, int newW, int newH) {

    }

    protected float dipToPx(float dip) {
        return dip * getContextInner(true).getResources().getDisplayMetrics().density + 0.5f;
    }

    public interface OnBeforeShowCallback {
        boolean onBeforeShow(View contentView, View anchorView, boolean hasShowAnimate);
    }

    public interface OnBlurOptionInitListener {
        void onCreateBlurOption(PopupBlurOption option);
    }

    public static abstract class OnDismissListener implements PopupWindow.OnDismissListener {
        public boolean onBeforeDismiss() {
            return true;
        }

        public void onDismissAnimationStart() {
        }
    }
    public interface OnPopupWindowShowListener {
        void onShowing();
    }

    public interface KeyEventListener {
        boolean onKey(KeyEvent keyEvent);
    }

    public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
        public CalledFromWrongThreadException(String msg) {
            super(msg);
        }
    }

}
